/*
此文件实现了基于 TCP 协议的服务器组件，包括：

	启动服务器并监听指定地址
	接受和处理客户端连接
	读取客户端数据并简单回声响应
	处理心跳保持连接活跃
	安全地关闭服务器和所有连接

2025/10/07
增加业务层处理接口handler，以及信息传递协议
*/
package tcp

import (
	"fmt"
	"gameServer/log"
	"gameServer/packet"
	"net"
	"sync"
	"time"
)

/* //* TCP服务器实现，提供网络连接管理、消息收发、心跳处理功能，支持并发安全的连接操作，通过Handler接口与业务层解耦 */

// 消息处理结构（接口定义：约定业务逻辑需实现的方法，可能有多种消息处理器）
type Handler interface {
	HandleMessage(conn net.Conn, pkt *packet.Packet) error
	HandleConnect(conn net.Conn) error
	HandleDisconnect(conn net.Conn) error
}

// Server Tcp 服务器  结构体
type Server struct {
	addr     string            // 网络地址 ip:port 作为listener参数
	listener net.Listener      // tcp服务器核心组件，监听tcp请求
	conns    map[net.Conn]bool // 管理所有客户端连接的切片，Conn就是链接接口，实例化确定是tcp还是udp，可以读写操作（c，s都用这个进行通信）
	mu       sync.RWMutex      // 读写锁，保护conns并发访问，避免多协程并发数据竞争
	isclosed bool              // 判断是否已经关闭服务器
	handler  Handler           // 消息处理句柄（持有业务层实现的Handler实例，没有业务层实例这里不能使用）
}

// 创建一个Tcp服务器
func NewServer(addr string, handler Handler) *Server {
	// 编译器自动判断结构体分配在什么位置，这里是堆上，因为跨文件了
	//go build -gcflags="-m" yourfile.go类似指令可以查看
	return &Server{
		addr:    addr,                    // 管理ip : port
		conns:   make(map[net.Conn]bool), // 键值对管理用户 是否链接
		handler: handler,                 // 消息处理器，由网关初始化
	}
}

// 设置消息处理器
func (s *Server) SetHandler(handler Handler) {
	s.handler = handler
}

// 启动Tcp服务
func (s *Server) Start() error {
	// 初始化服务器监听 返回一个监听器
	listerner, err := net.Listen("tcp", s.addr)
	if err != nil {
		return fmt.Errorf("failed to listen on %s: %v", s.addr, err)
	}
	s.listener = listerner
	log.Infof("TCP server start on %s", s.addr)
	// 开启协程监听客户端连接 非阻塞启动
	go s.acceptConnections()

	return nil
}

// 接收链接（添加）
func (s *Server) acceptConnections() {
	// 循环监听客户端连接
	for {
		// 阻塞方法，等有客户端连接才会继续执行，客户端用Dial创建conn
		conn, err := s.listener.Accept() // 这里报错有 closed，信号中断，网络错误，内存不足，文件描述符不足等
		if err != nil {
			// 这里单独协程，可能特别快，不如阻塞一小会，等数值改完就行
			time.Sleep(50 * time.Millisecond)
			if s.isClosed() {
				log.Infof("gameServe is over")
				return
			}
			log.Errorf("Failed to accept connection : %v", err)
			continue
		}
		// 并发安全
		s.mu.Lock()
		s.conns[conn] = true
		s.mu.Unlock()

		log.Infof("New connection from %s", conn.RemoteAddr())
		// 调用链接处理器
		if s.handler != nil {
			s.handler.HandleConnect(conn)
		}
		// 每个链接都用单独协程，使服务器能同时处理多个客户端连接请求
		go s.handleConnection(conn)
	}
}

// 处理连接  （这里暂时没定义数据结构（预计protobuf），因此还是一个框架，没处理粘包问题
func (s *Server) handleConnection(conn net.Conn) {
	// 延迟处理，如果连接断开，函数也就结束，这个时候删掉对应map节点，资源也会释放
	defer func() {
		conn.Close()
		s.mu.Lock()
		delete(s.conns, conn)
		s.mu.Unlock()
		// 调用断开连接处理器
		if s.handler != nil {
			s.handler.HandleDisconnect(conn)
		}
		log.Infof("Connection from %s closed", conn.RemoteAddr())
	}()
	// 定义收发数据的buffer 这次就比较正式了，4096，最合适的大小
	buffer := make([]byte, 4096)
	// 死循环处理逻辑
	for {
		// 设置读取超时限制，时间结束前没读完，Read返回net.Error，防止僵尸连接
		conn.SetReadDeadline(time.Now().Add(60 * time.Second))
		// read阻塞方法，等待客户端发送数据，两种可能错误，一种超时（可以认为长时间未发送数据，Read是阻塞的），一种读取失败（客户端主动关闭之类的）
		n, err := conn.Read(buffer)
		if err != nil {
			// 超时处理，err.(net.Error)是类型断言，判断是否这个类型，返回两个值，ok = true表示读取成功，Timeout就是判断是否超时
			if netErr, ok := err.(net.Error); ok && netErr.Timeout() {
				// 发送心跳回包并保持链接 现在就是正经心跳回包
				_, err := conn.Write(packet.PackHeartbeat())
				// 详细介绍这里的心跳机制：
				/*
					检测连接是否存活的关键设计，与超时处理紧密配合，确保服务器既能维持有效连接，又能及时清理真正失效的连接
					在 TCP 长连接中，客户端和服务器可能长时间没有业务数据交互,双方无法直接判断对方是否仍在线（可能是网络波动）
					当连接长时间无业务数据时，一方（通常是服务器）主动发送一个小数据包（心跳包） 给对方
					若收到对方回应（或能成功发送心跳包），则认为连接仍有效，继续保持；
					若发送失败或超时未收到回应，则判定连接已失效，主动关闭以释放资源。
				*/
				// 发送心跳包失败，结束连接
				if err != nil {
					return
				}
				continue
			}
			// 其他错误处理
			return
		}

		// 这里把处理数据单独拿出来，这个函数就是处理链接的，数据处理另一边去做

		// 解析数据包
		pkt, err := packet.Unpack(buffer[:n])
		if err != nil {
			log.Errorf("Failed to unpack message: %v", err)
			continue
		}
		// 处理消息（这个时候，就可以用json/protobuf解码了，不过在处理消息函数中）
		if s.handler != nil {
			if err := s.handler.HandleMessage(conn, pkt); err != nil {
				log.Errorf("Failed to handle message: %v", err)
			}
		}
		// // 处理接收到的数据，截取读到的数据长度
		// data := buffer[:n]
		// log.Infof("Received data from %s: %s", conn.RemoteAddr(), string(data))

		// // 简单回声响应
		// _, err = conn.Write(data)
		// if err != nil {
		// 	return
		// }
	}
}

// 停止tcp服务
func (s *Server) Stop() error {
	// 关闭监听器
	if s.listener != nil {
		log.Info("game server listener closed")
		s.listener.Close()
		s.isclosed = true
	}
	// 关闭所有链接，线程安全
	s.mu.Lock()
	for conn := range s.conns {
		conn.Close()
	}
	s.conns = make(map[net.Conn]bool)
	s.mu.Unlock()

	log.Infof("TCP server stop on %s", s.addr)
	return nil

}

// 获取服务器地址
func (s *Server) Addr() string {
	return s.addr
}

// 判断是否已经结束服务器
func (s *Server) isClosed() bool {
	return s.isclosed
}
